//*****************************************************************************
// JFab (http://code.google.com/p/jfab)
// Copyright (c) 2011-2012 JFab.org
// Admin jfab@jeffreythompson.net
//
// See the file "LICENSE.txt" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//*****************************************************************************

package org.jfab.swingui;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.swing.event.TreeModelEvent;
import javax.swing.event.TreeModelListener;
import javax.swing.tree.TreeModel;
import javax.swing.tree.TreePath;

import org.apache.commons.lang3.Validate;
import org.jfab.core.model.CGSpec;
import org.jfab.swingui.decorator.Decorator;
import org.jfab.swingui.decorator.DecoratorFactory;

/**
 * Provides a tree model implementation backed by a code generator
 * specification.
 */
public class CGSpecTreeModel implements TreeModel
{
    /** Tree model listeners. */
    private final List<TreeModelListener> _treeModelListeners = new ArrayList<TreeModelListener>();

    /** Code generator specification. */
    private final CGSpec _cgSpec;

    /** Map of object to decorator. */
    private Map<Object, Decorator<?>> _objectToDecorator = new HashMap<Object, Decorator<?>>();

    /** Decorator factory. */
    private DecoratorFactory _decoratorFactory;

    /** Icon manager. */
    private final IconManager _iconManager;

    /**
     * Construct this object with the given parameter.
     * 
     * @param cgSpec Code generator specification. (required)
     * @param iconManager Icon manager. (required)
     */
    public CGSpecTreeModel(final CGSpec cgSpec, final IconManager iconManager)
    {
        Validate.notNull(cgSpec, "cgSpec is null");
        Validate.notNull(iconManager, "iconManager is null");

        _cgSpec = cgSpec;
        _iconManager = iconManager;
    }

    @Override
    public void addTreeModelListener(final TreeModelListener listener)
    {
        _treeModelListeners.add(listener);
    }

    @Override
    public Object getChild(final Object parent, final int index)
    {
        Object answer = null;

        if (parent instanceof Decorator)
        {
            final Decorator<?> decorator = (Decorator<?>)parent;
            final Object object = decorator.getChild(index);
            answer = getDecoratorFor(object, decorator);
        }

        return answer;
    }

    @Override
    public int getChildCount(final Object parent)
    {
        int answer = 0;

        if (parent instanceof Decorator)
        {
            final Decorator<?> decorator = (Decorator<?>)parent;
            answer = decorator.getChildCount();
        }

        return answer;
    }

    @Override
    public int getIndexOfChild(final Object parent, final Object child)
    {
        int answer = -1;

        if (parent instanceof Decorator)
        {
            final Decorator<?> decorator = (Decorator<?>)parent;
            answer = decorator.getIndexOfChild(child);
        }

        return answer;
    }

    @Override
    public Object getRoot()
    {
        return getDecoratorFor(_cgSpec, null);
    }

    @Override
    public boolean isLeaf(final Object node)
    {
        boolean answer = true;

        if (node instanceof Decorator)
        {
            final Decorator<?> decorator = (Decorator<?>)node;
            answer = decorator.isLeaf();
        }

        return answer;
    }

    /**
     * @param decorator Decorator.
     * 
     * @return true if the list contained this.
     */
    public boolean removeNode(final Decorator<?> decorator)
    {
        boolean answer = false;

        if (decorator != null)
        {
            answer = decorator.removeNode();
        }

        fireTreeStructureChanged(getRoot());

        return answer;
    }

    @Override
    public void removeTreeModelListener(final TreeModelListener listener)
    {
        _treeModelListeners.remove(listener);
    }

    @Override
    public void valueForPathChanged(final TreePath path, final Object newValue)
    {
        // FIXME
    }

    /**
     * The only event raised by this model is TreeStructureChanged with the root
     * as path, i.e. the whole tree has changed.
     * 
     * @param oldRoot Old root.
     */
    private void fireTreeStructureChanged(final Object oldRoot)
    {
        final TreeModelEvent event = new TreeModelEvent(this,
                new Object[] { oldRoot });

        for (final TreeModelListener listener : _treeModelListeners)
        {
            listener.treeStructureChanged(event);
        }
    }

    /**
     * The only event raised by this model is TreeStructureChanged with the root
     * as path, i.e. the whole tree has changed.
     * 
     * @param oldRoot Old root.
     */
    // private void fireTreeStructureChanged(final CGSpec oldRoot)
    // {
    // final TreeModelEvent event = new TreeModelEvent(this,
    // new Object[] { oldRoot });
    //
    // for (final TreeModelListener listener : _treeModelListeners)
    // {
    // listener.treeStructureChanged(event);
    // }
    // }

    /**
     * @return the decoratorFactory
     */
    private DecoratorFactory getDecoratorFactory()
    {
        if (_decoratorFactory == null)
        {
            _decoratorFactory = new DecoratorFactory();
        }

        return _decoratorFactory;
    }

    /**
     * @param object Object.
     * @param parent Parent.
     * 
     * @return a decorator for the given parameter.
     */
    private Decorator<?> getDecoratorFor(final Object object,
            final Decorator<?> parent)
    {
        Decorator<?> answer = _objectToDecorator.get(object);

        if (answer == null)
        {
            answer = getDecoratorFactory().create(object, parent, _iconManager);
            _objectToDecorator.put(object, answer);
        }

        return answer;
    }
}
